home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
mtex
/
crop.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
29KB
|
821 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#include <gl/gl.h>
#include <math.h>
#include <device.h>
#include "types.h"
#include "colors.h"
#include "other.h"
quad_points old_crop;
long prev_rb_cx,prev_rb_cy;
long crop_button_indexes[4];
long old_state;
Boolean crop_1_to_1; /* Global variable! */
/* Last subscript varies most rapidly in C */
/* Square Rect No-rot 1:1 */
/* on on on on */
/* off off off off */
long crop_state_table[8][8] = { { 3, 0, 0, 2, 0, 4, 0, 7 }, /* Currently square */
{ 0, 1, 3, 0, 0, 5, 0, 8 }, /* Currently rectangle */
{ 0, 1, 0, 2, 0, 6, 0, 8 }, /* Currently quad */
{ 6, 0, 0, 5, 1, 0, 0, 7 }, /* Currently al_square */
{ 0, 4, 6, 0, 2, 0, 0, 8 }, /* Currently al_rect */
{ 0, 4, 0, 5, 3, 0, 0, 8 }, /* Currently al_quad */
{ 8, 0, 0, 8, 1, 0, 4, 0 }, /* Currently al_no_resam_square */
{ 0, 7, 6, 0, 2, 0, 5, 0 } }; /* Currently al_no_resam_rectangle */
/* S Re Ro 11 */
long lit_table[8][4] = { { 1, 0, 0, 0 }, /* Currently square */
{ 0, 1, 0, 0 }, /* Currently rectangle */
{ 0, 0, 0, 0 }, /* Currently quad */
{ 1, 0, 1, 0 }, /* Currently al_square */
{ 0, 1, 1, 0 }, /* Currently al_rect */
{ 0, 0, 1, 0 }, /* Currently al_quad */
{ 1, 0, 1, 1 }, /* Currently al_no_resam_square */
{ 0, 1, 1, 1 } }; /* Currently al_no_resam_rectangle */
/*...................................................................*/
/* Draw a quadrilateral as four lines, using color col...this is for rubberbanding */
void draw_qp(quad_points *qp,Colorindex col1, Colorindex col2)
{
point p1,p2,p3,p4,i1,i2,i3,i4;
float center_x,center_y;
color(col1);
SETPOINT(p1,(*qp)[0],(*qp)[1])
SETPOINT(p2,(*qp)[2],(*qp)[3])
SETPOINT(p3,(*qp)[4],(*qp)[5])
SETPOINT(p4,(*qp)[6],(*qp)[7])
draw_line(p1,p2);
draw_line(p2,p3);
draw_line(p3,p4);
draw_line(p4,p1);
center_x = (p1[0]+p2[0]+p3[0]+p4[0]) / 4.0;
center_y = (p1[1]+p2[1]+p3[1]+p4[1]) / 4.0;
i1[0] = p1[0] + SIGN2(p1[0],center_x);
i1[1] = p1[1] + SIGN2(p1[1],center_y);
i2[0] = p2[0] + SIGN2(p2[0],center_x);
i2[1] = p2[1] + SIGN2(p2[1],center_y);
i3[0] = p3[0] + SIGN2(p3[0],center_x);
i3[1] = p3[1] + SIGN2(p3[1],center_y);
i4[0] = p4[0] + SIGN2(p4[0],center_x);
i4[1] = p4[1] + SIGN2(p4[1],center_y);
color(col2);
draw_line(i1,i2);
draw_line(i2,i3);
draw_line(i3,i4);
draw_line(i4,i1);
}
/*...................................................................*/
/* Draw a rubberband quadrilateral into the overlay planes */
void draw_rb(long j)
{
long i;
Colorindex old_color;
short old_map1_red,old_map1_grn,old_map1_blu;
short old_map2_red,old_map2_grn,old_map2_blu;
quad_points *qp;
drawmode(PUPDRAW);
gconfig();
old_color = getcolor();
getmcolor(1,&old_map1_red,&old_map1_grn,&old_map1_blu);
getmcolor(2,&old_map2_red,&old_map2_grn,&old_map2_blu);
mapcolor(1,255,0,255);
mapcolor(2,0,0,0);
draw_qp(&old_crop,0,0);
qp = (quad_points *)(ipt[j].value);
draw_qp(qp,1,2);
color(old_color);
mapcolor(1,old_map1_red,old_map1_grn,old_map1_blu);
mapcolor(2,old_map2_red,old_map2_grn,old_map2_blu);
drawmode(NORMALDRAW);
gconfig();
for (i=0; i<8; i++) old_crop[i] = (*qp)[i];
}
/*...................................................................*/
void take_down_rb()
{
Colorindex old_color;
drawmode(OVERDRAW);
gconfig();
old_color = getcolor();
draw_qp(&old_crop,0,0);
color(old_color);
drawmode(NORMALDRAW);
gconfig();
}
/*...................................................................*/
/* Return +1 or -1 if p1-->tp is clockwise or counter-clockwise from p1-->p2 */
long in_out(point p1, point p2, point tp)
{
point v12,v1p;
SUB(p1,p2,v12);
SUB(p1,tp,v1p);
return(SIGN(CROSS(v12,v1p)));
}
/*...................................................................*/
/* Return TRUE or FALSE if cx,cy is inside or outside of quadrilateral *qp */
Boolean outside_qp(quad_points *qp, long cx, long cy)
{
point p1,p2,p3,p4,tp;
long s12,s23,s34,s41,sum;
SETPOINT(p1,(*qp)[0],(*qp)[1])
SETPOINT(p2,(*qp)[2],(*qp)[3])
SETPOINT(p3,(*qp)[4],(*qp)[5])
SETPOINT(p4,(*qp)[6],(*qp)[7])
SETPOINT(tp,(float)cx,(float)cy)
s12 = in_out(p1,p2,tp);
s23 = in_out(p2,p3,tp);
s34 = in_out(p3,p4,tp);
s41 = in_out(p4,p1,tp);
sum = s12+s23+s34+s41;
return ((sum != -4) && (sum != 4));
}
/*...................................................................*/
/* Extend atan2f to return OK even if x and or y are zero */
float arctan(float x, float y)
{
if ((x != 0.0) && (y != 0.0)) return(atan2f(y,x));
if ((x == 0.0) && (y == 0.0)) return(0.0);
if ((x > 0.0) && (y == 0.0)) return(0.0);
if ((x < 0.0) && (y == 0.0)) return(3.1415926535897932384626433);
if ((x == 0.0) && (y > 0.0)) return(3.1415926535897932384626433/2.0);
return(-3.1415926535897932384626433/2.0);
}
/*...................................................................*/
/* Normalize a vector...do nothing if zero length */
void normalize(point *v)
{
float len;
len = LENGTH(*v);
if (len == 0) return;
(*v)[0] /= len;
(*v)[1] /= len;
}
/*...................................................................*/
/* Return TRUE or FALSE if x,y is outside 512x512 image on-screen */
/* Also return TRUE if any side is too short. */
/* If TRUE, reset qp to original value to prevent rubberband going outside */
Boolean not_in_image(quad_points *qp, float x, float y)
{
long i,k,max_screen_x,max_screen_y;
Boolean reject;
float dx,dy,manhattan_dist,min_manhattan_dist,zoom;
if (improved_image->interior_xsize > improved_image->interior_ysize)
{
zoom = 512.0 / (float)improved_image->interior_xsize;
max_screen_x = 512 + IMAGE_BORDER;
max_screen_y = (long)(zoom * improved_image->interior_ysize + IMAGE_BORDER);
}
else
{
zoom = 512.0 / (float)improved_image->interior_ysize;
max_screen_x = (long)(zoom * improved_image->interior_xsize + IMAGE_BORDER);
max_screen_y = 512 + IMAGE_BORDER;
}
reject = (x < IMAGE_BORDER) || (x > 512+IMAGE_BORDER) ||
(y < IMAGE_BORDER) || (y > 512+IMAGE_BORDER);
min_manhattan_dist = 999999999.;
for (i=0; i<8; i+=2)
{
if ((i+2) < 8) k = i; else k = i-8;
dx = (*qp)[i+0] - (*qp)[k+2];
dy = (*qp)[i+1] - (*qp)[k+3];
manhattan_dist = ABS(dx) + ABS(dy);
min_manhattan_dist = MIN2(manhattan_dist,min_manhattan_dist);
}
reject |= (min_manhattan_dist < 10.);
if (reject)
{
for (k=0; k<8; k++) (*qp)[k] = old_crop[k];
return(TRUE);
}
else return(FALSE);
}
/*...................................................................*/
/*...useful someday?....
len_side1 = sqrtf(SQR((*qp)[0] - (*qp)[2]) + SQR((*qp)[1] - (*qp)[3]));
len_side2 = sqrtf(SQR((*qp)[2] - (*qp)[4]) + SQR((*qp)[3] - (*qp)[5]));
len_side3 = sqrtf(SQR((*qp)[4] - (*qp)[6]) + SQR((*qp)[5] - (*qp)[7]));
len_side4 = sqrtf(SQR((*qp)[6] - (*qp)[0]) + SQR((*qp)[7] - (*qp)[1]));
avg_side = (len_side1 + len_side2 + len_side3 + len_side4) / 4.0;
diag13 = sqrtf(SQR((*qp)[0] - (*qp)[4]) + SQR((*qp)[1] - (*qp)[5]));
diag24 = sqrtf(SQR((*qp)[2] - (*qp)[6]) + SQR((*qp)[3] - (*qp)[7]));
avg_diag = (diag13 + diag24) / 2.0;
*/
/*...................................................................*/
Boolean force_onscreen(quad_points *qp)
{
float minx,miny,maxx,maxy,movex1,movey1,movex2,movey2,zoom;
long i,max_screen_x,max_screen_y;
minx = MIN4((*qp)[0],(*qp)[2],(*qp)[4],(*qp)[6]);
miny = MIN4((*qp)[1],(*qp)[3],(*qp)[5],(*qp)[7]);
maxx = MAX4((*qp)[0],(*qp)[2],(*qp)[4],(*qp)[6]);
maxy = MAX4((*qp)[1],(*qp)[3],(*qp)[5],(*qp)[7]);
if (improved_image->interior_xsize > improved_image->interior_ysize)
{
zoom = 512.0 / (float)improved_image->interior_xsize;
max_screen_x = 512 + IMAGE_BORDER;
max_screen_y = (long)(zoom * improved_image->interior_ysize + IMAGE_BORDER);
}
else
{
zoom = 512.0 / (float)improved_image->interior_ysize;
max_screen_x = (long)(zoom * improved_image->interior_xsize + IMAGE_BORDER);
max_screen_y = 512 + IMAGE_BORDER;
}
movex1 = movex2 = 0.0;
movey1 = movey2 = 0.0;
if (minx < IMAGE_BORDER) movex1 = minx - IMAGE_BORDER;
if (miny < IMAGE_BORDER) movey1 = miny - IMAGE_BORDER;
if (maxx > max_screen_x) movex2 = maxx - 512+IMAGE_BORDER;
if (maxy > max_screen_x) movey2 = maxy - 512+IMAGE_BORDER;
if (movex1 != 0.0 && movex2 != 0.0) return(FALSE);
if (movey1 != 0.0 && movey2 != 0.0) return(FALSE);
for (i=0; i<4; i++)
{
(*qp)[2*i+0] -= (movex1 + movex2);
(*qp)[2*i+1] -= (movey1 + movex2);
}
return(TRUE);
}
/*...................................................................*/
void force_square(quad_points *qp)
{
point center,vect_v1,vect_v2,vect_v3,vect_v4,vect_typ_v;
center[0] = ((*qp)[0] + (*qp)[2] + (*qp)[4] + (*qp)[6]) / 4.0; /* Center = average of vertexes */
center[1] = ((*qp)[1] + (*qp)[3] + (*qp)[5] + (*qp)[7]) / 4.0;
vect_v1[0] = (*qp)[0] - center[0];
vect_v1[1] = (*qp)[1] - center[1];
vect_v2[0] = (*qp)[3] - center[1]; /* Reflect X <--> Y */
vect_v2[1] = -(*qp)[2] + center[0]; /* Reflect X <--> Y */
vect_v3[0] = -(*qp)[4] + center[0]; /* Reflect X <--> -X */
vect_v3[1] = -(*qp)[5] + center[1]; /* Reflect Y <--> -Y */
vect_v4[0] = -(*qp)[7] + center[1]; /* Reflect X <--> -Y */
vect_v4[1] = (*qp)[6] - center[0]; /* Reflect Y <--> -X */
vect_typ_v[0] = AVG4(vect_v1[0],vect_v2[0],vect_v3[0],vect_v4[0]);
vect_typ_v[1] = AVG4(vect_v1[1],vect_v2[1],vect_v3[1],vect_v4[1]);
(*qp)[0] = vect_typ_v[0] + center[0];
(*qp)[1] = vect_typ_v[1] + center[1];
(*qp)[2] = -vect_typ_v[1] + center[0];
(*qp)[3] = vect_typ_v[0] + center[1];
(*qp)[4] = -vect_typ_v[0] + center[0];
(*qp)[5] = -vect_typ_v[1] + center[1];
(*qp)[6] = vect_typ_v[1] + center[0];
(*qp)[7] = -vect_typ_v[0] + center[1];
force_onscreen(qp);
draw_rb(rb_ipt_index);
}
/*...................................................................*/
void rotate_by_angle(quad_points *qp, float rotation_angle)
{
point center,center_vertex;
long i;
float radius,prev_angle,new_angle,x,y;
center[0] = ((*qp)[0] + (*qp)[2] + (*qp)[4] + (*qp)[6]) / 4.0; /* Center = average of vertexes */
center[1] = ((*qp)[1] + (*qp)[3] + (*qp)[5] + (*qp)[7]) / 4.0;
for (i=0; i<4; i++)
{
center_vertex[0] = (*qp)[2*i+0] - center[0]; /* Vector to vertex from center */
center_vertex[1] = (*qp)[2*i+1] - center[1];
radius = LENGTH(center_vertex); /* Convert vertex to polar coord. */
prev_angle = arctan(center_vertex[0],center_vertex[1]);
new_angle = prev_angle + rotation_angle; /* Add rotation angle */
x = (*qp)[2*i+0] = center[0] + radius * cosf(new_angle); /* Convert to cartesian */
y = (*qp)[2*i+1] = center[1] + radius * sinf(new_angle);
if (not_in_image(qp,x,y)) return; /* Reset & abort if any vertex outside image */
}
}
/*...................................................................*/
/* Rotate quadrilateral around its center to track angular movement of cursor */
void rotate_quad(quad_points *qp, long cx, long cy)
{
float theta_center_cursor,theta_center_prevcursor,rotation_angle;
point center,center_cursor,center_prevcursor;
center[0] = ((*qp)[0] + (*qp)[2] + (*qp)[4] + (*qp)[6]) / 4.0; /* Center = average of vertexes */
center[1] = ((*qp)[1] + (*qp)[3] + (*qp)[5] + (*qp)[7]) / 4.0;
center_cursor[0] = center[0] - (float)cx; /* center_cursor = vector: center-->cursor */
center_cursor[1] = center[1] - (float)cy;
center_prevcursor[0] = center[0] - (float)prev_rb_cx; /* center_prevcursor = vector: center-->previous cursor */
center_prevcursor[1] = center[1] - (float)prev_rb_cy;
theta_center_cursor = arctan(center_cursor[0],center_cursor[1]); /* Angle to cursor */
theta_center_prevcursor = arctan(center_prevcursor[0],center_prevcursor[1]); /* Angle to previous cursor */
rotation_angle = theta_center_cursor - theta_center_prevcursor;
rotate_by_angle(qp,rotation_angle);
}
/*...................................................................*/
/* Move "nearest" vertex to be at cursor, but reset if x,y outside image */
void move_vertex_quad(quad_points *qp, long cx,long cy, long which_vert)
{
float x,y;
long i,k;
k = 2 * (which_vert-1); /* 0,2,4,6 for vertex 1,2,3,4 */
x = (*qp)[k] += (float)(cx - prev_rb_cx);
y = (*qp)[k+1] += (float)(cy - prev_rb_cy);
if (not_in_image(qp,x,y)) return;
}
/*...................................................................*/
/* Move all four qp vertexes by motion of cursor, abort & reset if any go too far */
void move_whole_quad(quad_points *qp, long cx, long cy)
{
float diff_x,diff_y,x,y;
long i;
diff_x = (float)(cx - prev_rb_cx);
diff_y = (float)(cy - prev_rb_cy);
for (i=0; i<8; i+=2)
{
x = (*qp)[i] += diff_x;
y = (*qp)[i+1] += diff_y;
if (not_in_image(qp,x,y)) return;
}
}
/*...................................................................*/
/* Distance squared from point to line. See Graphic Gems II, Page 10. */
/* pa and pb are points on line. Distance is to line, not line segment */
/* This works because crossproduct = twice area of abc triangle. */
float dist_sq_to_line(point pc, point pa, point pb)
{
point pcr,pbr;
float area2,ds;
SUB(pc,pa,pcr)
SUB(pb,pa,pbr)
area2 = CROSS(pcr,pbr);
ds = SQR(area2) / LENGTH_SQ(pbr);
return(ds);
}
/*...................................................................*/
/* Move one side of quadrilateral by motion of cursor along a vector which */
/* is more or less perpendicular to that side. Actual vector is average */
/* of the two adjacent sides. Reset if side moves outside image. */
void move_side_quad(quad_points *qp, long cx, long cy, long side)
{
float diff_x,diff_y,cursor_movement_length,cosine,distance_to_move;
point cursor_movement,p1,p2,p3,p4,movement_vect;
point adjacent_side1_vect,adjacent_side2_vect,avg_adjacent_side_vect;
long k1,k2,k3,k4;
diff_x = (float)(cx - prev_rb_cx);
diff_y = (float)(cy - prev_rb_cy);
SETPOINT(cursor_movement,diff_x,diff_y) /* Cursor movement vector */
cursor_movement_length = LENGTH(cursor_movement);
/* Which side is cx,cy nearest to? */
/* Side k1 k2 k3 k4 Previous Side Moving Side Next Side */
/* 1 3 0 1 2 qp[6,7]-->qp[0,1] qp[0,1]-->qp[2,3] qp[2,3]-->qp[4,5] */
/* 2 0 1 2 3 qp[0,1]-->qp[2,3] qp[2,3]-->qp[4,5] qp[4,5]-->qp[6,7] */
/* 3 1 2 3 0 qp[2,3]-->qp[4,5] qp[4,5]-->qp[6,7] qp[6,7]-->qp[0,1] */
/* 4 2 3 0 1 qp[4,5]-->qp[6,7] qp[6,7]-->qp[0,1] qp[0,1]-->qp[2,3] */
/* In general: p1 --> p2 p2 --> p3 p3 --> p4 */
k1 = (side+2) % 4; /* Copy chosen qp points into p1,p2,p3,p4 */
k2 = (side+3) % 4;
k3 = (side+0) % 4;
k4 = (side+1) % 4;
SETPOINT(p1, (*qp)[2*k1], (*qp)[2*k1+1])
SETPOINT(p2, (*qp)[2*k2], (*qp)[2*k2+1])
SETPOINT(p3, (*qp)[2*k3], (*qp)[2*k3+1])
SETPOINT(p4, (*qp)[2*k4], (*qp)[2*k4+1])
SUB(p1,p2,adjacent_side1_vect) /* We will move in average of adjacent sides */
SUB(p4,p3,adjacent_side2_vect)
AVG(adjacent_side1_vect,adjacent_side2_vect,avg_adjacent_side_vect)
normalize(&cursor_movement);
normalize(&avg_adjacent_side_vect);
cosine = DOT(cursor_movement,avg_adjacent_side_vect); /* Projection */
distance_to_move = cosine * cursor_movement_length; /* Movement vector length */
MULT(avg_adjacent_side_vect,distance_to_move,movement_vect) /* Movement vector itself */
(*qp)[2*k2] = p2[0] + movement_vect[0]; /* Add movement */
(*qp)[2*k2+1] = p2[1] + movement_vect[1];
if (not_in_image(qp,(*qp)[2*k2],(*qp)[2*k2+1])) return; /* Abort & Reset if outside */
(*qp)[2*k3] = p3[0] + movement_vect[0]; /* Add movement */
(*qp)[2*k3+1] = p3[1] + movement_vect[1];
not_in_image(qp,(*qp)[2*k3],(*qp)[2*k3+1]); /* Reset if outside */
}
/*...................................................................*/
/* Move vertex to resize rectangle...keep adjacent sides parallel to others */
void resize_rect(quad_points *qp, long cx, long cy, long which_vert)
{
long k1,k2,k3,k4;
float len_new_diagonal,cosine,length_41,length_43,x,y,diff_x,diff_y,length_cm;
point p1,p2,p3,p4,new_p1,new_p2,new_p3,new_diagonal,p4_p1_vect,p4_p3_vect;
point cursor_move,old_diagonal;
/* Move "which_vert" vertex to cx,cy */
/* Determine adjacent vertexes */
/* They must be where cx,cy project on to original opposite sides */
k1 = (which_vert+2) % 4; /* Copy chosen qp points into p1,p2,p3,p4 */
k2 = (which_vert+3) % 4; /* "which_vert = p2 */
k3 = (which_vert+0) % 4;
k4 = (which_vert+1) % 4; /* ..p1 ... p4 */
SETPOINT(p1, (*qp)[2*k1], (*qp)[2*k1+1]) /* . /. */
SETPOINT(p2, (*qp)[2*k2], (*qp)[2*k2+1]) /* . / . */
SETPOINT(p3, (*qp)[2*k3], (*qp)[2*k3+1]) /* . / . */
SETPOINT(p4, (*qp)[2*k4], (*qp)[2*k4+1]) /* p2 ./. p3 */
/* / . */
diff_x = (float)(cx - prev_rb_cx); /* * . */
diff_y = (float)(cy - prev_rb_cy);
if ((crop_constraints == SQUARE) ||
(crop_constraints == AL_SQUARE) ||
(crop_constraints == AL_NO_RESAM_SQUARE))
{
SETPOINT(cursor_move,diff_x,diff_y)
SUB(p2,p4,old_diagonal)
length_cm = LENGTH(cursor_move);
normalize(&cursor_move);
normalize(&old_diagonal);
cosine = DOT(cursor_move,old_diagonal);
diff_x = old_diagonal[0] * length_cm * cosine;
diff_y = old_diagonal[1] * length_cm * cosine;
}
x = (*qp)[2*k2] += diff_x;
y = (*qp)[2*k2+1] += diff_y;
if (not_in_image(qp,x,y)) return; /* Abort if outside */
SETPOINT(new_p2,x,y)
SUB(new_p2,p4,new_diagonal)
len_new_diagonal = LENGTH(new_diagonal);
SUB(p1,p4,p4_p1_vect)
SUB(p3,p4,p4_p3_vect)
normalize(&new_diagonal);
normalize(&p4_p1_vect);
normalize(&p4_p3_vect);
cosine = DOT(p4_p1_vect,new_diagonal);
length_41 = cosine * len_new_diagonal;
MULT(p4_p1_vect,length_41,new_p1)
x = (*qp)[2*k1] = new_p1[0] + p4[0];
y = (*qp)[2*k1+1] = new_p1[1] + p4[1];
if (not_in_image(qp,x,y)) return;
cosine = DOT(p4_p3_vect,new_diagonal);
length_43 = cosine * len_new_diagonal;
MULT(p4_p3_vect,length_43,new_p3)
x = (*qp)[2*k3] = new_p3[0] + p4[0];
y = (*qp)[2*k3+1] = new_p3[1] + p4[1];
not_in_image(qp,x,y);
}
/*...................................................................*/
/*...................................................................*/
/* Return 1,2,3,4 if corresponding side is close to cx,cy */
/* Return 0 if not. "Close" = 1/4 of larger diagonal distance */
long near_side(quad_points *qp, long cx, long cy)
{
float d12,d23,d34,d41,limit,min_d,shorter_across;
point cursor,p1,p2,p3,p4;
SETPOINT(cursor,(float)cx,(float)cy)
SETPOINT(p1,(*qp)[0],(*qp)[1])
SETPOINT(p2,(*qp)[2],(*qp)[3])
SETPOINT(p3,(*qp)[4],(*qp)[5])
SETPOINT(p4,(*qp)[6],(*qp)[7])
d12 = dist_sq_to_line(cursor,p1,p2);
d23 = dist_sq_to_line(cursor,p2,p3);
d34 = dist_sq_to_line(cursor,p3,p4);
d41 = dist_sq_to_line(cursor,p4,p1);
min_d = MIN4(d12,d23,d34,d41);
shorter_across = MIN2(d12+d34,d23+d41);
limit = shorter_across / 10.;
if (min_d > limit) return(0);
if (min_d == d12) return(1);
if (min_d == d23) return(2);
if (min_d == d34) return(3);
return(4);
}
/*...................................................................*/
/* Return 1,2,3,4 if corresponding qp vertex is close to cx,cy */
/* Return 0 if not. "Close" = 1/4 of larger diagonal distance */
long near_vertex(quad_points *qp, long cx, long cy)
{
float d1,d2,d3,d4,d12,d23,d34,d41,shortest_side,radius,min_dist_to_vertex;
point cursor,p1,p2,p3,p4;
SETPOINT(cursor,(float)cx,(float)cy)
SETPOINT(p1,(*qp)[0],(*qp)[1])
SETPOINT(p2,(*qp)[2],(*qp)[3])
SETPOINT(p3,(*qp)[4],(*qp)[5])
SETPOINT(p4,(*qp)[6],(*qp)[7])
d1 = DISTSQ(p1,cursor);
d2 = DISTSQ(p2,cursor);
d3 = DISTSQ(p3,cursor);
d4 = DISTSQ(p4,cursor);
d12 = MAN_DIST(p1,p2);
d23 = MAN_DIST(p2,p3);
d34 = MAN_DIST(p3,p4);
d41 = MAN_DIST(p4,p1);
shortest_side = MIN4(d12,d23,d34,d41);
radius = SQR(shortest_side) / 40.;
min_dist_to_vertex = MIN4(d1,d2,d3,d4);
if (min_dist_to_vertex > radius) return(0);
if (min_dist_to_vertex == d1) return(1);
if (min_dist_to_vertex == d2) return(2);
if (min_dist_to_vertex == d3) return(3);
return(4);
}
/*...................................................................*/
void rb_action(Boolean resize, Boolean side_move, Boolean rotate, Boolean vert_move,
quad_points *qp, long cx, long cy)
{
long nearest_v,nearest_s;
if (rotate)
{
if (outside_qp(qp,cx,cy))
{
rotate_quad(qp,cx,cy);
crop_1_to_1 = FALSE;
return;
}
}
nearest_v = near_vertex(qp,prev_rb_cx,prev_rb_cy);
if (vert_move && (nearest_v > 0))
{
move_vertex_quad(qp,cx,cy,nearest_v);
crop_1_to_1 = FALSE;
return;
}
nearest_s = near_side(qp,prev_rb_cx,prev_rb_cy);
if (side_move && (nearest_v == 0) && (nearest_s > 0))
{
move_side_quad(qp,cx,cy,nearest_s);
crop_1_to_1 = FALSE;
return;
}
if (resize && (nearest_v > 0))
{
resize_rect(qp,cx,cy,nearest_v);
crop_1_to_1 = FALSE;
return;
}
move_whole_quad(qp,cx,cy);
}
/*...................................................................*/
/* Update procedure for rubberband widget. */
void move_rb(long j, long dev_mask, long cx, long cy)
{
Boolean near,far,down,up,d;
quad_points *qp;
long nearest;
near = (dev_mask & NEAR) != 0;
far = (dev_mask & FAR) != 0;
down = (dev_mask & LEFTMOUSE_DOWN) != 0;
up = (dev_mask & LEFTMOUSE_UP) != 0;
if (near && !down && !up) up = !(down = getbutton(LEFTMOUSE));
if ((ipt[j].state == IDLE_UP) && near) /* Enter image area...button up */
{
if (!down && !up) down = getbutton(LEFTMOUSE);
if (down)
ipt[j].state = HL_DOWN;
else
ipt[j].state = HL_UP;
ipt[j].input_mask1 = LEFTMOUSE_DOWN | FAR;
}
else if ((ipt[j].state == HL_UP) && far) /* Leave image area...button up */
{
ipt[j].state = IDLE_UP;
ipt[j].input_mask1 = LEFTMOUSE_DOWN | NEAR;
}
else if ((ipt[j].state == HL_UP) && down) /* Button goes down in image */
{
ipt[j].state = HL_DOWN;
ipt[j].input_mask1 = LEFTMOUSE_UP | NEAR | FAR;
prev_rb_cx = cx;
prev_rb_cy = cy;
}
else if ((ipt[j].state == HL_DOWN) && up) /* Button goes up in image */
{
ipt[j].state = HL_UP;
ipt[j].input_mask1 = LEFTMOUSE_DOWN | FAR;
}
else if ((ipt[j].state == HL_DOWN) && far) /* Leave image area...button down */
{
ipt[j].state = IDLE_UP;
ipt[j].input_mask1 = NEAR;
}
if (ipt[j].state == HL_DOWN) /* Take Action! Button down while in image */
{
qp = (quad_points *)(ipt[j].value);
/* Actions = resize_rectangle, move_side, rotate, move_vertex */
switch(ipt[j].data) /* Constraints determine action */
{
case(0): printf("Rubberband w/constraints = 0\n");
case(SQUARE): rb_action(1,0,1,0,qp,cx,cy); break;
case(RECTANGLE): rb_action(1,1,1,0,qp,cx,cy); break;
case(QUAD): rb_action(0,1,1,1,qp,cx,cy); break;
case(AL_SQUARE): rb_action(1,0,0,0,qp,cx,cy); break;
case(AL_RECTANGLE): rb_action(1,1,0,0,qp,cx,cy); break;
case(AL_QUAD): rb_action(0,1,0,1,qp,cx,cy); break;
case(AL_NO_RESAM_SQUARE): rb_action(0,0,0,0,qp,cx,cy); break;
case(AL_NO_RESAM_RECTANGLE): rb_action(0,0,0,0,qp,cx,cy); break;
default: printf("Unknown constraints: %d\n",ipt[j].data);
}
draw_rb(j);
prev_rb_cx = cx;
prev_rb_cy = cy;
}
}
/*...................................................................*/
void fix_indicators()
{
long i,k;
for (i=0; i<4; i++)
{
k = crop_button_indexes[i];
ipt[k].data = lit_table[crop_constraints-1][i];
ipt[k].redisplay_ui_proc(k);
}
}
/*...................................................................*/
Boolean fix_shape()
{
quad_points *qp;
float zoom,xsize,ysize;
long largest_dim;
/* Zoom is ratio of qp coordinate scale (512 wide) to texmap scale (interior_xsize wide) */
largest_dim = MAX2(improved_image->interior_xsize,improved_image->interior_ysize);
zoom = 512.0/(float)largest_dim;
qp = (quad_points *)(ipt[rb_ipt_index].value);
xsize = (float)crop_interior_x * zoom;
ysize = (float)crop_interior_y * zoom;
(*qp)[0] = (*qp)[6] = IMAGE_BORDER + 256. - xsize / 2.0;
(*qp)[1] = (*qp)[3] = IMAGE_BORDER + 256. - ysize / 2.0;;
(*qp)[2] = (*qp)[4] = IMAGE_BORDER + 256. + xsize / 2.0;;
(*qp)[5] = (*qp)[7] = IMAGE_BORDER + 256. + ysize / 2.0;;
if (xsize > 512. || ysize > 512.)
{
crop_1_to_1 = FALSE;
(*qp)[0] = (*qp)[1] = (*qp)[3] = (*qp)[6] = IMAGE_BORDER+50.;
(*qp)[2] = (*qp)[4] = (*qp)[5] = (*qp)[7] = IMAGE_BORDER+512.-50.;
draw_rb(rb_ipt_index);
return(FALSE);
}
draw_rb(rb_ipt_index);
return(TRUE);
}
/*...................................................................*/
void crop_sq(long j)
{
old_state = crop_constraints;
ipt[j].data ^= 1; /* Toggle button state */
crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 0];
ipt[rb_ipt_index].data = crop_constraints;
if (crop_constraints == 0)
{
printf("Crop_sq state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
exit(2);
}
fix_indicators();
if (ipt[j].data == 1) force_square((quad_points *)(ipt[rb_ipt_index].value));
}
/*...................................................................*/
void crop_re(long j)
{
old_state = crop_constraints;
ipt[j].data ^= 1; /* Toggle button state */
crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 2];
ipt[rb_ipt_index].data = crop_constraints;
if (crop_constraints == 0)
{
printf("Crop_re state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
exit(2);
}
fix_indicators();
if (ipt[j].data == 1) fix_shape();
}
/*...................................................................*/
void crop_nr(long j)
{
old_state = crop_constraints;
ipt[j].data ^= 1; /* Toggle button state */
crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 4];
ipt[rb_ipt_index].data = crop_constraints;
if (crop_constraints == 0)
{
printf("Crop_nr state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
exit(2);
}
fix_indicators();
if (ipt[j].data == 1) fix_shape();
}
/*...................................................................*/
void crop_11(long j)
{
ipt[j].data ^= 1; /* Toggle button state */
if (ipt[j].data == 1)
if (!fix_shape())
{
ipt[j].data = 0;
return;
}
old_state = crop_constraints;
crop_1_to_1 = ipt[j].data;
crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 6];
ipt[rb_ipt_index].data = crop_constraints;
fix_indicators();
if (crop_constraints == 0)
{
printf("Crop_11 state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
exit(2);
}
}
/*...................................................................*/
void cleanup_crop()
{
take_down_rb();
turn_off_all_widgets();
}
/*...................................................................*/